﻿using static StackExchange.Redis.Role;

namespace anydata.Entitys
{
    public class XChange : EntityBase
    {
        public string instanceId
        {
            get => this["instanceId"] as string;
            set => this["instanceId"] = value;
        }

        public string belongId
        {
            get => this["belongId"] as string;
            set => this["belongId"] = value;
        }

        public string changeTime
        {
            get => this["changeTime"] as string;
            set => this["changeTime"] = value;
        }

        public string thingId
        {
            get => this["thingId"] as string;
            set => this["thingId"] = value;
        }

        public string propId
        {
            get => this["propId"] as string;
            set => this["propId"] = value;
        }

        public double? before
        {
            get => this["before"] as double?;
            set => this["before"] = value;
        }

        public double? after
        {
            get => this["after"] as double?;
            set => this["after"] = value;
        }

        public double? change
        {
            get => this["change"] as double?;
            set => this["change"] = value;
        }

        public int? symbol
        {
            get => this["symbol"] as int?;
            set => this["symbol"] = value;
        }

        public string? snapshotId
        {
            get => this["snapshotId"] as string;
            set => this["snapshotId"] = value;
        }

        public XChange(CompareContext context, string _thingId, string _propId, double _before, double _after, string? _snapshotId)
        {
            var changeValue = _after - _before;
            instanceId = context.Instance.id;
            name = context.Instance.title;
            belongId = context.Token.BelongId.ToString();
            createUser = context.Token.UserId.ToString();
            updateUser = context.Token.UserId.ToString();
            changeTime = context.ChangeTime;
            thingId = _thingId;
            propId = _propId;
            before = _before;
            after = _after;
            symbol = Math.Sign(changeValue);
            change = Math.Abs(changeValue);
            snapshotId = _snapshotId;
            labels = new List<string>();
            id = "snowId()";
        }

        public void SetChangeDimension(EntityBase entity, List<FormField> sourceFields)
        {
            foreach (var sourceField in sourceFields)
            {
                if (sourceField.code.Trim().Replace("_", "") != "id" &&
                    sourceField.code.Trim().Replace("_", "") != "extensions")
                {
                    var value = entity.GetStringValue(sourceField);
                    if (value is not null)
                    {
                        this[sourceField.code] = value;
                    }
                }
            }
        }

        public static List<XChange> CompareThing(CompareContext context, EntityBase? oldThing, EntityBase? newThing, string? snapshotId)
        {
            var result = new List<XChange>();
            if (context.TargetFields.Count == 0 || context.SourceFields.Count == 0)
            {
                return result;
            }
            if (oldThing is null && newThing is not null)
            {
                foreach (var field in context.TargetFields)
                {
                    var after = newThing.GetDoubleValue(field);
                    var change = new XChange(context, newThing.id, field.code, 0, after, snapshotId);
                    change.SetChangeDimension(newThing, context.SourceFields);
                    result.Add(change);
                }
            }
            if (oldThing != null && newThing != null)
            {
                foreach (var field in context.Fields)
                {
                    var after = newThing[field.code];
                    if (after is null)
                    {
                        newThing[field.code] = oldThing[field.code];
                    }
                }
                foreach (var field in context.TargetFields)
                {
                    var before = oldThing.GetDoubleValue(field);
                    var after = newThing.GetDoubleValue(field);
                    if (before != after)
                    {
                        var change = new XChange(context, newThing.id, field.code, before, after, snapshotId);
                        change.SetChangeDimension(oldThing, context.SourceFields);
                        result.Add(change);
                    }
                }
                var labels = new List<string>();
                foreach (var sourceField in context.SourceFields)
                {
                    var dimensionBefore = oldThing.GetStringValue(sourceField);
                    var dimensionAfter = newThing.GetStringValue(sourceField);
                    if (dimensionBefore != dimensionAfter)
                    {
                        labels.Add(sourceField.code);
                    }
                }
                if (labels.Count > 0)
                {
                    foreach (var targetField in context.TargetFields)
                    {
                        var afterValue = newThing.GetDoubleValue(targetField);
                        if (oldThing is not null)
                        {
                            var beforeChange = new XChange(context, newThing.id, targetField.code, afterValue, 0, snapshotId);
                            beforeChange.SetChangeDimension(oldThing, context.SourceFields);
                            beforeChange.labels = labels;
                            result.Add(beforeChange);
                        }
                        var afterChange = new XChange(context, newThing.id, targetField.code, 0, afterValue, snapshotId);
                        afterChange.SetChangeDimension(newThing, context.SourceFields);
                        afterChange.labels = labels;
                        result.Add(afterChange);
                    } 
                }
            }
            if (oldThing is not null && newThing is null)
            {
                foreach (var field in context.TargetFields)
                {
                    var before = oldThing.GetDoubleValue(field);
                    var change = new XChange(context, oldThing.id, field.code, before, 0, snapshotId);
                    change.SetChangeDimension(oldThing, context.SourceFields);
                    result.Add(change);
                }
            }
            return result.Where(item => item.symbol != 0).ToList();
        }
    }
}
